﻿using System;
using System.Collections.Generic;
using System.Linq;
using Engine;
using Engine.Graphics;
using Game;

namespace MFTrain
{
    public class MFRailBlock : Block
    {
        public const int Index = 543;

        public BoundingBox[][] boundingBoxes = new BoundingBox[10][];

        public BoundingBox[] breakerBoxes = new BoundingBox[6];

        public BlockMesh[] m_blockMeshes = new BlockMesh[10];

        public Texture2D texture;

        public Texture2D trainTexture;

        public BlockMesh[] trainBreakerMeshes = new BlockMesh[6];

        public override void Initialize()
        {
            base.Initialize();
            BlockMesh blockMesh = new BlockMesh();
            texture = ContentManager.Get<Texture2D>("MF/Train/Blocks");
            trainTexture = ContentManager.Get<Texture2D>("MF/Train/Train");
            Model model = ContentManager.Get<Model>("MF/Train/TrainBreaker");
            Matrix boneAbsoluteTransform = BlockMesh.GetBoneAbsoluteTransform(model.FindMesh("TrainBreaker").ParentBone);
            trainBreakerMeshes[0] = new BlockMesh();
            trainBreakerMeshes[0].AppendModelMeshPart(model.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateTranslation(0.5f, 0f, 0.5f), makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[0].AppendModelMeshPart(model.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateTranslation(0.5f, 0f, 0.5f), makeEmissive: false, flipWindingOrder: true, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[1] = new BlockMesh();
            trainBreakerMeshes[1].AppendModelMeshPart(model.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateTranslation(-0.5f, 0f, 0.5f) * Matrix.CreateFromYawPitchRoll(MathUtils.DegToRad(90f), 0f, 0f), makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[1].AppendModelMeshPart(model.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateTranslation(-0.5f, 0f, 0.5f) * Matrix.CreateFromYawPitchRoll(MathUtils.DegToRad(90f), 0f, 0f), makeEmissive: false, flipWindingOrder: true, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[2] = new BlockMesh();
            trainBreakerMeshes[2].AppendModelMeshPart(model.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateFromYawPitchRoll(MathUtils.DegToRad(135f), 0f, 0f) * Matrix.CreateTranslation(0.5f, 0f, 0.5f), makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[2].AppendModelMeshPart(model.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateFromYawPitchRoll(MathUtils.DegToRad(135f), 0f, 0f) * Matrix.CreateTranslation(0.5f, 0f, 0.5f), makeEmissive: false, flipWindingOrder: true, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[3] = new BlockMesh();
            trainBreakerMeshes[3].AppendModelMeshPart(model.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateFromYawPitchRoll(MathUtils.DegToRad(45f), 0f, 0f) * Matrix.CreateTranslation(0.5f, 0f, 0.5f), makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[3].AppendModelMeshPart(model.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateFromYawPitchRoll(MathUtils.DegToRad(45f), 0f, 0f) * Matrix.CreateTranslation(0.5f, 0f, 0.5f), makeEmissive: false, flipWindingOrder: true, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[4] = new BlockMesh();
            trainBreakerMeshes[4].AppendModelMeshPart(model.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateTranslation(0.5f, 0.5f, 0.5f), makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[4].AppendModelMeshPart(model.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateTranslation(0.5f, 0.5f, 0.5f), makeEmissive: false, flipWindingOrder: true, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[5] = new BlockMesh();
            trainBreakerMeshes[5].AppendModelMeshPart(model.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateTranslation(-0.5f, 0.5f, 0.5f) * Matrix.CreateFromYawPitchRoll(MathUtils.DegToRad(90f), 0f, 0f), makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[5].AppendModelMeshPart(model.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateTranslation(-0.5f, 0.5f, 0.5f) * Matrix.CreateFromYawPitchRoll(MathUtils.DegToRad(90f), 0f, 0f), makeEmissive: false, flipWindingOrder: true, doubleSided: false, flipNormals: false, Color.White);
            BlockMeshVertex[] items = new BlockMeshVertex[4]
            {
                new BlockMeshVertex
                {
                    Color = Color.White,
                    TextureCoordinates = new Vector2(0f, 0.0625f),
                    Position = new Vector3(0f, 0.01f, 1f)
                },
                new BlockMeshVertex
                {
                    Color = Color.White,
                    TextureCoordinates = new Vector2(0.0625f, 0.0625f),
                    Position = new Vector3(1f, 0.01f, 1f)
                },
                new BlockMeshVertex
                {
                    Color = Color.White,
                    TextureCoordinates = new Vector2(0.0625f, 0f),
                    Position = new Vector3(1f, 0.01f, 0f)
                },
                new BlockMeshVertex
                {
                    Color = Color.White,
                    TextureCoordinates = new Vector2(0f, 0f),
                    Position = new Vector3(0f, 0.01f, 0f)
                }
            };
            ushort[] items2 = new ushort[6] { 2, 1, 0, 0, 3, 2 };
            blockMesh.Vertices.AddRange(items);
            blockMesh.Indices.AddRange(items2);
            BlockMesh blockMesh2 = new BlockMesh();
            blockMesh2.AppendBlockMesh(blockMesh);
            blockMesh2.TransformTextureCoordinates(Matrix.CreateTranslation(new Vector3(0.875f, 0.875f, 0f)));
            Matrix matrix = Matrix.CreateTranslation(new Vector3(0.5f, 0f, 0.5f));
            Matrix matrix2 = Matrix.CreateTranslation(new Vector3(-0.5f, 0f, -0.5f));
            for (int i = 0; i < 4; i++)
            {
                m_blockMeshes[i] = new BlockMesh();
                m_blockMeshes[i].AppendBlockMesh(blockMesh2);
                m_blockMeshes[i].TransformPositions(matrix2 * Matrix.CreateRotationY((float)Math.PI / 2f * (float)i) * matrix);
                m_blockMeshes[i].GenerateSidesData();
            }

            blockMesh2 = new BlockMesh();
            blockMesh2.AppendBlockMesh(blockMesh);
            blockMesh2.TransformTextureCoordinates(Matrix.CreateTranslation(new Vector3(0.8125f, 0.875f, 0f)));
            m_blockMeshes[4] = new BlockMesh();
            m_blockMeshes[4].AppendBlockMesh(blockMesh2);
            m_blockMeshes[4].GenerateSidesData();
            m_blockMeshes[5] = new BlockMesh();
            m_blockMeshes[5].AppendBlockMesh(blockMesh2);
            m_blockMeshes[5].TransformPositions(matrix2 * Matrix.CreateRotationY((float)Math.PI / 2f) * matrix);
            m_blockMeshes[5].GenerateSidesData();
            blockMesh = new BlockMesh();
            items = new BlockMeshVertex[4]
            {
                new BlockMeshVertex
                {
                    Color = Color.White,
                    TextureCoordinates = new Vector2(0f, 0.0625f),
                    Position = new Vector3(0f, 0.01f, 1f)
                },
                new BlockMeshVertex
                {
                    Color = Color.White,
                    TextureCoordinates = new Vector2(0.0625f, 0.0625f),
                    Position = new Vector3(1f, 0.01f, 1f)
                },
                new BlockMeshVertex
                {
                    Color = Color.White,
                    TextureCoordinates = new Vector2(0.0625f, 0f),
                    Position = new Vector3(1f, 1.01f, 0f)
                },
                new BlockMeshVertex
                {
                    Color = Color.White,
                    TextureCoordinates = new Vector2(0f, 0f),
                    Position = new Vector3(0f, 1.01f, 0f)
                }
            };
            items2 = new ushort[12]
            {
                2, 1, 0, 0, 1, 2, 0, 3, 2, 2,
                3, 0
            };
            blockMesh.Vertices.AddRange(items);
            blockMesh.Indices.AddRange(items2);
            blockMesh.TransformTextureCoordinates(Matrix.CreateTranslation(new Vector3(0.8125f, 0.875f, 0f)));
            for (int j = 6; j < 10; j++)
            {
                m_blockMeshes[j] = new BlockMesh();
                m_blockMeshes[j].AppendBlockMesh(blockMesh);
                m_blockMeshes[j].TransformPositions(matrix2 * Matrix.CreateRotationY((float)Math.PI / 2f * (float)j) * matrix);
            }

            BoundingBox[] array = new BoundingBox[1]
            {
                new BoundingBox(0f, 0f, 0f, 1f, 0.01f, 1f)
            };
            for (int k = 0; k < 6; k++)
            {
                boundingBoxes[k] = array;
            }

            boundingBoxes[6] = new BoundingBox[4]
            {
                new BoundingBox(0f, 0f, 0f, 1f, 0.01f, 1f),
                new BoundingBox(0f, 0f, 0.25f, 1f, 0.25f, 1f),
                new BoundingBox(0f, 0f, 0.5f, 1f, 0.5f, 1f),
                new BoundingBox(0f, 0f, 0.75f, 1f, 0.75f, 1f)
            };
            boundingBoxes[7] = new BoundingBox[4]
            {
                new BoundingBox(0f, 0f, 0f, 1f, 0.01f, 1f),
                new BoundingBox(0.25f, 0f, 0f, 1f, 0.25f, 1f),
                new BoundingBox(0.5f, 0f, 0f, 1f, 0.5f, 1f),
                new BoundingBox(0.75f, 0f, 0f, 1f, 0.75f, 1f)
            };
            boundingBoxes[8] = new BoundingBox[4]
            {
                new BoundingBox(0f, 0f, 0f, 1f, 0.01f, 1f),
                new BoundingBox(0f, 0f, 0f, 1f, 0.25f, 0.75f),
                new BoundingBox(0f, 0f, 0f, 1f, 0.5f, 0.5f),
                new BoundingBox(0f, 0f, 0f, 1f, 0.75f, 0.25f)
            };
            boundingBoxes[9] = new BoundingBox[4]
            {
                new BoundingBox(0f, 0f, 0f, 1f, 0.01f, 1f),
                new BoundingBox(0f, 0f, 0f, 0.75f, 0.25f, 1f),
                new BoundingBox(0f, 0f, 0f, 0.5f, 0.5f, 1f),
                new BoundingBox(0f, 0f, 0f, 0.25f, 0.75f, 1f)
            };
            breakerBoxes[0] = new BoundingBox(0f, 0f, 0.4f, 1f, 0.5f, 0.6f);
            breakerBoxes[1] = new BoundingBox(0.4f, 0f, 0f, 0.6f, 0.5f, 1f);
            breakerBoxes[2] = new BoundingBox(0f, 0f, 0f, 1f, 0.5f, 1f);
            breakerBoxes[3] = new BoundingBox(0f, 0f, 0f, 1f, 0.5f, 1f);
            breakerBoxes[4] = new BoundingBox(0f, 0.5f, 0.4f, 1f, 1f, 0.6f);
            breakerBoxes[5] = new BoundingBox(0.4f, 0.5f, 0f, 0.6f, 1f, 1f);
        }

        public override BoundingBox[] GetCustomCollisionBoxes(SubsystemTerrain terrain, int value)
        {
            return IsBreaker(Terrain.ExtractData(value)) ? boundingBoxes[GetRailType(Terrain.ExtractData(value))].Concat(new BoundingBox[1] { breakerBoxes[GetBreakerType(Terrain.ExtractData(value))] }).ToArray() : boundingBoxes[GetRailType(Terrain.ExtractData(value))];
        }

        public override void DrawBlock(PrimitivesRenderer3D primitivesRenderer, int value, Color color, float size, ref Matrix matrix, DrawBlockEnvironmentData environmentData)
        {
            BlocksManager.DrawFlatOrImageExtrusionBlock(primitivesRenderer, value, size, ref matrix, texture, color, isEmissive: false, environmentData);
        }

        public override void GenerateTerrainVertices(BlockGeometryGenerator generator, TerrainGeometry geometry, int value, int x, int y, int z)
        {
            generator.GenerateMeshVertices(this, x, y, z, m_blockMeshes[GetRailType(Terrain.ExtractData(value))], Color.White, null, geometry.GetGeometry(texture).SubsetAlphaTest);
            if (IsBreaker(Terrain.ExtractData(value)))
            {
                generator.GenerateMeshVertices(this, x, y, z, trainBreakerMeshes[GetBreakerType(Terrain.ExtractData(value))], Color.White, null, geometry.GetGeometry(trainTexture).SubsetOpaque);
            }
        }

        public static int GetRailType(int data)
        {
            return data & 0xF;
        }

        public static int SetRailType(int data, int type)
        {
            return (data & -16) | (type & 0xF);
        }

        public static bool IsCorner(int type)
        {
            return type >> 2 == 0;
        }

        public static bool IsDirectionX(int type)
        {
            return (type & 1) == 0;
        }

        public static bool IsBreaker(int data)
        {
            return ((data >> 6) & 1) == 1;
        }

        public static bool IsHorizontal(int type)
        {
            return type == 4 || type == 5;
        }

        public static int SetBreaker(int data)
        {
            return data | 0x40;
        }

        public static int ClearBreaker(int data)
        {
            return data & 0x3FBF;
        }

        public static int GetBreakerType(int data)
        {
            if (IsHorizontal(GetRailType(data)))
            {
                return (!IsDirectionX(GetRailType(data))) ? 1 : 0;
            }

            if (IsCorner(GetRailType(data)))
            {
                return IsDirectionX(GetRailType(data)) ? 2 : 3;
            }

            return IsDirectionX(GetRailType(data)) ? 4 : 5;
        }

        public override BlockPlacementData GetPlacementValue(SubsystemTerrain subsystemTerrain, ComponentMiner componentMiner, int value, TerrainRaycastResult raycastResult)
        {
            float num = MathUtils.Abs(MathUtils.RadToDeg(componentMiner.ComponentCreature.ComponentCreatureModel.EyeRotation.ToYawPitchRoll().X));
            BlockPlacementData result = default(BlockPlacementData);
            result.CellFace = raycastResult.CellFace;
            result.Value = Terrain.MakeBlockValue(543, 0, SetRailType(0, (num > 45f && num < 135f) ? 5 : 4));
            return result;
        }

        public override void GetDropValues(SubsystemTerrain subsystemTerrain, int oldValue, int newValue, int toolLevel, List<BlockDropValue> dropValues, out bool showDebris)
        {
            showDebris = true;
            dropValues.Add(new BlockDropValue
            {
                Value = 543,
                Count = 1
            });
        }

        public static bool CanConnectTo(int railType, int direction)
        {
            if (IsCorner(railType))
            {
                return direction == railType || direction == ((railType + 1) & 3);
            }

            return IsDirectionX(railType) ^ !IsDirectionX(direction);
        }
    }
}